package ogmios.dialogs;

import ogmios.components.munches.*;
import ogmios.components.*;
import ogmios.core.*;


import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.undo.*;
import javax.swing.text.*;
import javax.swing.event.*;

import java.io.*;
import java.util.*;
import java.util.List;


/**
 * A scripting interface for editting and running Ogmios macros.
 * <br>
 * This may be eventually merged into Ogmios' editor.
 */
public class MacroScripter extends JFrame implements Context, ChainableListener
{
 public static int MAX_UNDO_LEVEL = 1024;
 private OgmiosTextPane code;
 private JTextArea output;
 private JButton runMacro;
 private String currentWorkingDirectory;
 private JSplitPane splitter;
 private String path = null;
 private JLabel name;
 private boolean saveNeeded = false;
 private static final String MACRO_FILE_FILTER = " .ocm | 'Ogmios Macros'";
 private final DocumentListener CHANGE_LISTENER = 
   new DocumentListener()
   {
     public void changedUpdate(DocumentEvent e)
      {
      }
      public void insertUpdate(DocumentEvent e)
      {
        saveNeeded(e);
      }
      public void removeUpdate(DocumentEvent e)
      {
       saveNeeded(e);
      }
     public void saveNeeded(DocumentEvent e)
     {
       saveNeeded = true;
       code.getDocument().removeDocumentListener(this);
       name.setText("*" + OgmiosUtilities.getFileName(path));
     }
   };

 public static void main(String[] args)
 {
   new MacroScripter().setVisible(true);
 }

 public MacroScripter()
 { 
   this(null);
 }

 public MacroScripter(String macroPath)
 {
   setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
   setTitle("Macro Scripter");
   JMenuBar menu = new JMenuBar();
   setJMenuBar(menu);
   Toolkit kit;
   int mask = (kit=Toolkit.getDefaultToolkit()).getMenuShortcutKeyMask();
   JMenu fileMenu = new JMenu("Macros");
    fileMenu.setMnemonic(KeyEvent.VK_M);
    JMenuItem newMacro = new JMenuItem("New Macro");
     newMacro.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, mask));
    JMenuItem openMacro = new JMenuItem("Open Macro");
     openMacro.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, mask));
    JMenuItem saveMacro = new JMenuItem("Save Macro");
     saveMacro.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, mask));
    JMenuItem saveAsMacro = new JMenuItem("Save As Macro");
    JMenuItem renameMacro = new JMenuItem("Rename Macro");
     renameMacro.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, mask));
   menu.add(fileMenu);
    fileMenu.add(newMacro);
    fileMenu.add(openMacro);
    fileMenu.add(saveMacro);
    fileMenu.add(saveAsMacro);
    fileMenu.add(renameMacro);
    newMacro.addActionListener(
     new ActionListener()
     {
       public void actionPerformed(ActionEvent e)
       {
         if (canChange())
         {
           clearUndo();
           if (!saveNeeded)
           {
            code.getDocument().removeDocumentListener(CHANGE_LISTENER); 
          }
           else
            saveNeeded = false;
           name.setText("No Macro");
           path = null;
           code.setText("");
           clearUndo();
         }
       }
     }
     );
  
    openMacro.addActionListener(
     new ActionListener()
     {
       public void actionPerformed(ActionEvent e)
       {
         if (canChange())
        {
          JFileChooser fileDialog = OgmiosFileChooser.getFileDialog(MACRO_FILE_FILTER);
          fileDialog.setMultiSelectionEnabled(false);
          fileDialog.setDialogTitle("Open Macro");
          if (fileDialog.showOpenDialog(MacroScripter.this) == JFileChooser.APPROVE_OPTION)
          {
            try
            {
             setTo(fileDialog.getSelectedFile().getCanonicalFile(), true);
            }
             catch(Throwable ex)
             {
                OgmiosUtilities.showErrorBox(MacroScripter.this, ex, "Macro:Open Error");
             }
          }
        }
       }
     }
    );

    saveMacro.addActionListener(
     new ActionListener()
     {
       public void actionPerformed(ActionEvent e)
       {
         save();
       }
     }
    );

    saveAsMacro.addActionListener(
     new ActionListener()
    {
      public void actionPerformed(ActionEvent e)
      {
        if (path == null)
        {
          if (code.getText().length() > 0)
          {
            save();
          } 
        }
        else
        {
          JFileChooser fileChooser = OgmiosFileChooser.getFileDialog(MACRO_FILE_FILTER);
          fileChooser.setMultiSelectionEnabled(false);
          if (fileChooser.showSaveDialog(MacroScripter.this) == JFileChooser.APPROVE_OPTION)
          {
             try
            {
              File file = fileChooser.getSelectedFile().getCanonicalFile();
              path = file.getPath();
              name.setText(OgmiosUtilities.getFileName(path));
              save();
            }
             catch(Throwable ex)
             {
               OgmiosUtilities.showErrorBox(MacroScripter.this, ex, "Macro:Save As Error");
             }
          }
        }
      }
    }
    );

    renameMacro.addActionListener(
     new ActionListener()
    {
      public void actionPerformed(ActionEvent e)
      {
        if (path == null)
        {
          if (code.getText().length() > 0)
          {
            save();
          }
        }
        else
        {
          JFileChooser fileChooser = OgmiosFileChooser.getFileDialog(MACRO_FILE_FILTER);
          fileChooser.setMultiSelectionEnabled(false);
          if (fileChooser.showSaveDialog(MacroScripter.this) == JFileChooser.APPROVE_OPTION)
          {
             try
            {
              save();

              File file = fileChooser.getSelectedFile().getCanonicalFile();
              if (new File(path).renameTo(file))
              {
               path = file.getPath();
               name.setText(OgmiosUtilities.getFileName(path));
              }
              else
              {
                JOptionPane.showMessageDialog(MacroScripter.this, "Could not rename file.", "Macro:Rename Error", JOptionPane.ERROR_MESSAGE);
              }
            }
             catch(Throwable ex)
             {
               OgmiosUtilities.showErrorBox(MacroScripter.this, ex, "Macro:Save As Error");
             }
          }
        }
      }
    }
    );
    
    currentWorkingDirectory = System.getProperty("user.home");
    code = new OgmiosTextPane();
    output = new JTextArea();
    output.setEditable(false);
    name = new JLabel("No Macro",JLabel.CENTER);
    runMacro = new JButton("Run Macro");
    runMacro.addActionListener(
     new ActionListener()
     {
      public void actionPerformed(ActionEvent e)
      {
        output.setText("");
        runMacro.setEnabled(false);
        try
        {
         new MacroExecutor().execute(MacroScripter.this, getMacroStepsFromLines(code.getText()), MacroScripter.this);
        }
         catch(Throwable ex)
         {
           chainComplete(null);
         } 
      }
     }
     );
    
    addWindowListener(
      new WindowAdapter()
      {
        public void windowClosing(WindowEvent e)
        {
          if (canChange())
          {
             MacroScripter.this.dispose();
          }
        }
       }
    );

    splitter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, new JScrollPane(OgmiosUtilities.undoable(code)), new JScrollPane(output));
    Container contents = getContentPane();
    contents.add(splitter);
    contents.add(name, BorderLayout.NORTH);
    contents.add(runMacro, BorderLayout.SOUTH);
    Dimension screenSize = kit.getScreenSize();
    screenSize.width /= 2.5;
    screenSize.height /= 1.5;
    setSize(screenSize);

    splitter.setDividerLocation(0.75);
    splitter.setResizeWeight(0.5);   

   if (macroPath != null)
   {
     File newMacroFile = new File(macroPath);
     if (newMacroFile.exists() && newMacroFile.isFile())
     {
        try
        {
          setTo(newMacroFile.getCanonicalFile(), true);
        }
        catch(Throwable ex)
        {
          OgmiosUtilities.showErrorBox(MacroScripter.this, ex, "Macro:Open Error");
        }
     }
   }
 }

 public void chainComplete(ChainEvent e)
 {
      runMacro.setEnabled(true);
     currentWorkingDirectory = System.getProperty("user.home"); 
 }
 
 public void print(String message)
 {
  output.append(message);
 }

 public void println(String message)
 {
  output.append(message);
  newLine();
 }

 public void newLine()
 {
  output.append("\n");
 }

 public String getCurrentWorkingDirectory()
 {
  return currentWorkingDirectory;
 }

 public void setTo(File newFile, boolean alreadySaved) throws IOException, BadLocationException
 {
  code.setText("");
  FileInputStream stream = new FileInputStream(newFile);
  code.getEditorKit().read(stream, code.getDocument(), 0);
  Document doc;
  (doc=code.getDocument()).putProperty(DefaultEditorKit.EndOfLineStringProperty, System.getProperty("line.separator"));
  stream.close();
  code.setEditable(true);
  code.setCaretPosition(0);
  path = newFile.getPath();
  if (alreadySaved)
  {
   name.setText(OgmiosUtilities.getFileName(path));
   saveNeeded=false;
   doc.addDocumentListener(CHANGE_LISTENER);
  }
  else
  {
    name.setText("*" + OgmiosUtilities.getFileName(path));
    saveNeeded = true;
  }
  clearUndo();
   
 }

 public void setTo(String fileName, String content, boolean alreadySaved)
 {
  code.setText(content);
  Document doc;
  (doc=code.getDocument()).putProperty(DefaultEditorKit.EndOfLineStringProperty, System.getProperty("line.separator"));
  code.setCaretPosition(0);
  path = fileName;
  if (alreadySaved)
  {
   name.setText(OgmiosUtilities.getFileName(path));
   saveNeeded=false;
   doc.addDocumentListener(CHANGE_LISTENER);
  }
  else
  {
    name.setText("*" + OgmiosUtilities.getFileName(path));
    saveNeeded = true;
  }
  clearUndo();
 }



 public boolean save()
 {
   String path = getPath();
   if (path == null)
   {
     return false;
   }
  try
  {
   FileWriter writer = new FileWriter(path);
   Document doc = code.getDocument();
   code.getEditorKit().write(writer, doc, 0, doc.getLength());
   writer.close();     
   if (saveNeeded)
   {
     saveNeeded = false;
     doc.addDocumentListener(CHANGE_LISTENER);
   }
   name.setText(OgmiosUtilities.getFileName(path));
   }
   catch(Throwable ex)
   {
     OgmiosUtilities.showErrorBox(this, ex, "Macro:Save Error");
     return false;
   }
   return true;   
 }

 public boolean canChange()
 {
   if (path == null && code.getText().length() != 0 || saveNeeded)
   {
     switch(JOptionPane.showConfirmDialog(this, "Do you want to save your unsaved work?", "Save", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE))
     {
       case JOptionPane.YES_OPTION: return save();
       case JOptionPane.NO_OPTION: break;
       default: return false;
     }
   }
    return true;
 }

 public String getPath()
 {
   if (path == null && code.getText().length() != 0)
   {
     JFileChooser fileChooser = OgmiosFileChooser.getFileDialog(MACRO_FILE_FILTER);
     fileChooser.setMultiSelectionEnabled(false);
     fileChooser.setDialogTitle("Save Untitled Macro");
     if (fileChooser.showSaveDialog(MacroScripter.this)==JFileChooser.APPROVE_OPTION)
     {
       try
       {
         File selectedFile = fileChooser.getSelectedFile().getCanonicalFile();
         FileWriter writer = new FileWriter(selectedFile);
         writer.write(code.getText());
         writer.close();
         path = selectedFile.getPath();
         name.setText(OgmiosUtilities.getFileName(path));
         saveNeeded=false;
         return path;
       }
        catch(Throwable ex)
        {
          OgmiosUtilities.showErrorBox(this, ex, "Macro:Save Error");
        }
     }
   }
   return path;
 }


 public void clearUndo()
 {
  Document document = code.getDocument();
  UndoManager undo = ((UndoManager)document.getProperty(OgmiosUtilities.UNDOABLE_EDIT_LISTENER));
  undo.setLimit(100);
  undo.discardAllEdits();
  undo.setLimit(MAX_UNDO_LEVEL);
 }

 /**
 * Splits several steps of a macro which are delimited by new lines.
 */
 public static List<String> getMacroStepsFromLines(String lines)
 {
   List<String> steps = new ArrayList<String>();
   Collections.addAll(steps, lines.split("[\\r\\n]+"));
   return steps;
 }

 /**
 * Splits several steps of a macro which are delimited by spaces, and are quoted.
 */
 public static List<String> getMacroStepsFromLine(String line)
 {
   return CommandManager.splitCommandLine(line);
 }

 public void setCurrentWorkingDirectory(String currentWorkingDirectory)
 {
   File cwd = OgmiosUtilities.resolvedFile(currentWorkingDirectory, this.currentWorkingDirectory);
   if (cwd.exists() && cwd.isDirectory())
   {
    currentWorkingDirectory = cwd.getAbsolutePath();
    this.currentWorkingDirectory=(currentWorkingDirectory);
   }   
 }

}
